package JVM.第02篇_类的加载篇;

/**
 * 这里，我们使用了“被破坏”这个词来形容上述不符合双亲委派模型原则的行为，
 * 但这里“被破坏”并不一定是带有贬义的。
 * 只要有明确的目的和充分的理由，突破旧有原则无疑是种创新。
 *
 *
 * 正如:破坏双亲委派机制3的OSGi中的类加载器的设计不符合传统的双亲委派的类加载器架构
 * 且业界对其为了实现热部署而带来的额外的高复杂度还存在不少争议，
 * 但对这方面有了解的技术人员基本还是能达成一个共识，认为 OSGi 中对类加载器的运用是值得学习的，
 * 完全弄懂了0SG1的实现，就算是掌握了类加载器的精粹。
 *
 * 第一次破坏双亲委派机制:
 * 双亲委派模型的第一次“被破坏”其实发生在双亲委派模型出现之前
 * 即JDK 1. 2面世以前的“远古”时代。
 * 由于双亲委派模型在JDK 1.2之后才被引入，但是类加载器的概念和抽象类
 * java.lang.ClassLoader则在Java的第一个版本中就已经存在，
 * 面对已经存在的用户自定义类加载器的代码，
 * Java设计者们引入双亲委派模型时不得不做出一些妥协，为了兼容这些已有代码，
 * 无法再以技术手段避免loadClass()被子类覆盖的可能性，
 * 只能在JDK1.2之后的java.lang.ClassLoader中添加一个 新的protected方法findClass(),
 * 并引导用户编写的类加载逻辑时尽可能去重写这个方法，
 * 而不是在loadClass()中编写代码。
 * 上节我们已经分析过loadClass()方法，双亲委派的具体逻辑就实现在这里面，
 * 按照loadClass()方 法的逻辑
 * 如果父类加载失败，会自动调用自己的findClass()方法来完成加载，
 * 这样既不影响用户按照自己的意愿去加载类，又可以保证新写出来的类加载器是符合双亲委派规则的。
 *
 *
 * 第二次破坏双亲委派机制:线程上下文类加载器
 * 双亲委派模型的第二次“被破坏”是由这个模型自身的缺陷导致的，
 * 双亲委派很好地解决了各个类加载器协作时基础类型的一致性问题
 * (越基础的类由越上层的加载器进行加载)，
 * 基础类型之所以被称为“基础”，是因为它们总是作为被用户代码继承、调用的API存在，
 * 但程序设计往往没有绝对不变的完美规则，如果有基础类型又要调用回用户的代码，那该怎么办呢?
 * 这并非是不可能出现的事情，一个典型的例子便是JNDI服务，
 * JNDI现在 已经是Java的标准服务，它的代码由启动类加载器来完成加载(在JDK 1.3时加入到rt.jar的)，
 * 肯定属于Java中很基础的类型了。
 * 但JNDI存在的目的就是对资源进行查找和集中管理，
 * 它需要调用由其他厂商实现并部署在应用程序的ClassPath下的
 * JNDI服务提供者接口(ServiceProvider Interface, SPI) 的代码，
 * 现在问题来了，启动类加载器是绝不可能认识、加载这些代码的，那该怎么办?
 * (SPI: 在Java平台中，通常把核心类rt.jar中提供外部服务、可由应用层自行实现的接口称为SPI)
 *
 * 为了解决这个困境，Java的设计团队只好引入了一个不太优雅的设计:
 *      线程上下文类加载器( Thread Context ClassLoader) 。
 *  这个类加载器可以通过java.lang.Thread类的setContextClassLoader( )方法进行设置，
 *  如果创建线程时还未设置，它将会从父线程中继承一个，
 *  如果在应用程序的全局范围内都没有设置过的话，那这个类加载器默认就是应用程序类加载器。
 *
 * 第三次破坏双亲委派机制:
 * 双亲委派模型的第三次“被破坏”是由于用户对程序动态性的追求而导致的。
 * 如:代码热替书(Hot Swap) 、模块热部署(Hot Deployment) 等
 * IBM公司主导的JSR-291 (即OSGi R4.2) 实现模块化热部署的关键是它自定义的类加载器机制的实现，
 * 每一个程序 模块(OSGi 中称为Bundle) 都有一个自己的类加载器，
 * 当需要更换一个Bundle时，就把Bundle连同类加载器一起换掉以实现代码的热替换。
 * 在0SGi环境下，类加载器不再双亲委派模型推荐的树状结构，而是进一步发展为更加复杂的网状结构。
 * 当收到类加载请求时，OSGi将按照下面的顺序进行类搜索:
 * 1)将以java.*开头的类，委派给父类加载器加载。
 * 2)否则，将委派列表名单内的类，委派给父类加载器加载。
 * 3)否则，将Import列表中的类，委派给Export这个类的Bundle的类加载器加载。
 * 4)否则，查找当前Bundle的ClassPath, 使用自己的类加载器加载。
 * 5)否则，查找类是否在自己的Fragment Bundle中，
 *      如果在，则委派给Fragment Bundle的类加载器加载。
 * 6)否则，查找Dynamic Import列表的Bundle, 委派给对应Bundle的类加载器加载
 * 7)否则，类查找失败。
 *
 */
public class C11_破坏双亲委派 {
    public static void main(String[] args) {
        //线程上下文的加载器
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        System.out.println(contextClassLoader);
    }

}
